import * as fs from 'fs-extra';
import * as path from 'path';
import { DocumentInfo, DocumentBriefing } from './types';
import { AliBailianService, AliBailianConfig } from './ali-bailian-service';

export class BriefingService {
  private aliBailianService: AliBailianService;
  private briefingsPath: string;

  constructor(aliBailianConfig?: AliBailianConfig) {
    // 初始化阿里云百炼服务
    if (aliBailianConfig) {
      this.aliBailianService = new AliBailianService(aliBailianConfig);
    } else {
      // 从环境变量获取配置
      const apiKey = process.env.ALI_BAILIAN_API_KEY;
      if (!apiKey) {
        throw new Error('ALI_BAILIAN_API_KEY environment variable is required');
      }
      this.aliBailianService = new AliBailianService({
        apiKey,
        baseUrl: process.env.ALI_BAILIAN_BASE_URL,
      });
    }
    this.briefingsPath = path.join(process.cwd(), '.cb-document-helper', 'briefings');
  }

  async generateBriefing(documentInfo: DocumentInfo): Promise<string> {
    try {
      // 确保briefings目录存在
      await fs.ensureDir(this.briefingsPath);      // 准备用于生成摘要的提示
      const prompt = this.createSummaryPrompt(documentInfo);
      
      // 使用阿里云百炼生成摘要
      let briefing = await this.aliBailianService.generateText(
        prompt,
        'qwen-plus-latest', // 使用 qwen-plus-latest 模型
        {
          temperature: 0.3, // 较低的temperature以获得更一致的摘要
          top_p: 0.9,
          top_k: 40,
          max_tokens: 800, // 限制生成长度
        }
      );
      
      // 清理模型输出中的思考过程标签
      briefing = this.cleanBriefingResponse(briefing);
      
      // 确保简介不超过600字
      const truncatedBriefing = this.truncateToBriefing(briefing);
      
      // 保存到磁盘
      await this.saveBriefing(documentInfo.filePath, truncatedBriefing, documentInfo.lastModified);
      
      return truncatedBriefing;
    } catch (error) {
      console.error(`Error generating briefing for ${documentInfo.filePath}:`, error);
      throw error;
    }
  }


  async getBriefing(filePath: string): Promise<string> {
    try {
      const briefingFile = this.getBriefingFilePath(filePath);
    //   console.log(`Checking briefing for ${filePath} at ${briefingFile}`);
      
      // 检查简介文件是否存在
      if (await fs.pathExists(briefingFile)) {
        const briefingData: DocumentBriefing = await fs.readJson(briefingFile);

        // 检查文件是否已修改 - 使用时间戳比较
        const stat = await fs.stat(filePath);
        const fileModifiedTimestamp = stat.mtime.getTime();
        const briefingModifiedTimestamp = briefingData.fileModifiedTimestamp;
        
        // console.log(`File timestamp: ${fileModifiedTimestamp}, Briefing timestamp: ${briefingModifiedTimestamp}`);
        if (fileModifiedTimestamp <= briefingModifiedTimestamp) {
          return briefingData.briefing;
        }
      }

      // 如果简介不存在或文件已修改，重新生成
      const content = await fs.readFile(filePath, 'utf-8');
      const matter = require('gray-matter');
      const parsed = matter(content);
      
      const documentInfo: DocumentInfo = {
        filePath,
        content,
        frontMatter: parsed.data,
        lastModified: (await fs.stat(filePath)).mtime,
      };

      return await this.generateBriefing(documentInfo);
    } catch (error) {
      console.error(`Error getting briefing for ${filePath}:`, error);
      return `Error loading briefing for ${filePath}: ${error}`;
    }
  }


  private createSummaryPrompt(documentInfo: DocumentInfo): string {
    const frontMatterInfo = Object.keys(documentInfo.frontMatter).length > 0 
      ? `\n\nFrontmatter metadata:\n${JSON.stringify(documentInfo.frontMatter, null, 2)}`
      : '';

      // 此处，提示中为“摘要长度不超过500字”，但实际假设生成摘要长度有600字，留有一些余量。
    return `请概括以下markdown文档，要求：
1. 长度不超过500字
2. 突出主要内容和关键信息
3. 仅关注文档内容本身，而非文档结构。
4. 直接陈述文档内容，不要将文字浪费在在诸如“此文档描述了”的语句上。
5. 如果有frontmatter元数据，请在概况中提及

文档路径: ${documentInfo.filePath}${frontMatterInfo}

文档内容:
${documentInfo.content}

请生成摘要:`;
  }

  private cleanBriefingResponse(text: string): string {
    // 移除qwen模型的思考过程标签
    let cleaned = text;
    
    // 移除 <think>...</think> 标签及其内容
    cleaned = cleaned.replace(/<think>[\s\S]*?<\/think>/g, '');
    
    // 移除可能的其他模型标签
    cleaned = cleaned.replace(/<\/?[a-zA-Z][^>]*>/g, '');
    
    // 清理多余的空白字符
    cleaned = cleaned.replace(/\n\s*\n/g, '\n').trim();
    
    return cleaned;
  }

  private truncateToBriefing(text: string): string {
    // 如果文本超过600字，智能截断
    if (text.length <= 600) {
      return text;
    }

    // 尝试在句子边界截断
    const sentences = text.split(/[。！？.!?]/);
    let result = '';
    
    for (const sentence of sentences) {
      if ((result + sentence).length > 580) { // 留一些余量
        break;
      }
      result += sentence + (sentence.trim() ? '。' : '');
    }

    // 如果结果太短，直接截断到600字
    if (result.length < 100) {
      result = text.substring(0, 597) + '...';
    }

    return result.trim();
  }

  private async saveBriefing(filePath: string, briefing: string, fileModified: Date): Promise<void> {
    const briefingFile = this.getBriefingFilePath(filePath);
    
    const briefingData: DocumentBriefing = {
      filePath,
      briefing,
      lastUpdated: new Date(),
      fileModified, // 保持向后兼容
      fileModifiedTimestamp: fileModified.getTime(), // 新增时间戳字段
    };

    await fs.writeJson(briefingFile, briefingData, { spaces: 2 });
  }

  private getBriefingFilePath(filePath: string): string {
    // 创建基于文件路径的唯一文件名
    const hash = this.hashString(filePath);
    return path.join(this.briefingsPath, `${hash}.json`);
  }

  private hashString(str: string): string {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // 转换为32位整数
    }
    return Math.abs(hash).toString(36);
  }

  async clearBriefings(): Promise<void> {
    try {
      if (await fs.pathExists(this.briefingsPath)) {
        await fs.emptyDir(this.briefingsPath);
      }
    } catch (error) {
      console.error('Error clearing briefings:', error);
    }
  }

  async getAllBriefings(): Promise<DocumentBriefing[]> {
    try {
      if (!(await fs.pathExists(this.briefingsPath))) {
        return [];
      }

      const files = await fs.readdir(this.briefingsPath);
      const briefings: DocumentBriefing[] = [];

      for (const file of files) {
        if (file.endsWith('.json')) {
          try {
            const briefingData = await fs.readJson(path.join(this.briefingsPath, file));
            briefings.push(briefingData);
          } catch (error) {
            console.error(`Error reading briefing file ${file}:`, error);
          }
        }
      }

      return briefings;
    } catch (error) {
      console.error('Error getting all briefings:', error);
      return [];
    }
  }
}
